﻿#region MIT
/*
Engine Of Evermore (http://code.google.com/p/engineofevermore/)
Copyright (c) 2008, 2009 Michael Woerister

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.ComponentModel;
using System.Xml.Linq;
using System.Diagnostics;

namespace EngineOfEvermore.Imaging
{
    /// <summary>
    /// Creates Frames for an ImageMap based on a given FrameSize (CellSize),
    /// a stride between two frames and a start position for the generation.
    /// </summary>
    public class ImageMapCellFrameGenerator : IFrameGenerator
    {
        #region Properties

        /// <summary>
        /// The start position of the cell generation.
        /// </summary>
        [Description("The start position of the cell generation.")]
        [Category("Cell-Based Frame Generator")]
        public Point Start { get; set; }


        /// <summary>
        /// The size in pixels of one cell.
        /// </summary>
        [Description( "The size in pixels of one cell." )]
        [Category( "Cell-Based Frame Generator" )]
        public Size CellSize { get; set; }


        /// <summary>
        /// The distance in pixels between two cells.
        /// </summary>
        [Description( "The distance in pixels between two cells." )]
        [Category( "Cell-Based Frame Generator" )]
        public Size CellStride { get; set; }

        #endregion

        #region ImageMap.IFrameGenerator Members
        /// <summary>
        /// This human-readable directoryName is displayed in the ImageMapEditor.
        /// </summary>
        [Browsable(false)]
        public String FriendlyName { get { return "Cell-Based Frame Generator"; } }

        /// <summary>
        /// Returns the Xml directoryName for this generator.
        /// </summary>
        [Browsable(false)]
        public String XmlName { get { return "CELL"; } }

        /// <summary>
        /// Generates frames with the current settings.
        /// </summary>
        public IEnumerable<Rectangle> GenerateFrames( Bitmap sourceBitmap )
        {
            if ( CellSize.Width <= 0 || CellSize.Height <= 0 )
            {
                throw new Exception( "CellSize " + CellSize.ToString() + " is empty" );
            }

            if ( Start.X < 0 || Start.Y < 0 )
            {
                throw new Exception( "Start " + Start.ToString() + " is negative" );
            }

            int width = sourceBitmap.Width;
            int height = sourceBitmap.Height;

            int cellCountX = ( width - Start.X ) / ( CellSize.Width + CellStride.Width );
            int cellCountY = ( height - Start.Y ) / ( CellSize.Height + CellStride.Height );

            int actualWidth = ( CellSize.Width + CellStride.Width ) * cellCountX;
            int actualHeight = ( CellSize.Height + CellStride.Height ) * cellCountY;

            if ( Start.X + actualWidth + CellSize.Width <= width )
            {
                ++cellCountX;
            }

            if ( Start.Y + actualHeight + CellSize.Height <= height )
            {
                ++cellCountY;
            }

            Rectangle rect = new Rectangle();

            for ( int y = 0; y < cellCountY; ++y )
            {
                for ( int x = 0; x < cellCountX; ++x )
                {
                    rect.X = Start.X + x * ( CellSize.Width + CellStride.Width );
                    rect.Y = Start.Y + y * ( CellSize.Height + CellStride.Height );
                    rect.Size = CellSize;

                    yield return rect;
                }
            }
        }

        /// <summary>
        /// Returns the Xml Representation for the frame generator.
        /// </summary>
        /// <example>
        /// Here is an example.
        /// <![CDATA[
        /// <FrameGenerator type="CELL">
        ///     <param directoryName="cell-size">{ Width=32; Height=32 }</param>
        ///     <param directoryName="cell-stride">{ Width=0; Height=0 }</param>
        ///     <param directoryName="start">{ X=0; Y=0 }</param>
        /// </FrameGenerator>
        /// ]]>
        /// </example>
        [Browsable( false )]
        public XElement XmlRepresentation
        {
            get
            {
                TypeConverter sizeConv = TypeDescriptor.GetConverter( typeof( Size ) );
                TypeConverter pointConv = TypeDescriptor.GetConverter( typeof( Point ) );

                return
                    new XElement( "FrameGenerator",
                        new XAttribute( "type", XmlName ),
                        new XElement( "param",
                            new XAttribute( "name", "cell-size" ),
                                sizeConv.ConvertToInvariantString( CellSize )
                                ),
                        new XElement( "param",
                            new XAttribute( "name", "cell-stride" ),
                                sizeConv.ConvertToInvariantString( CellStride )
                                ),
                        new XElement( "param",
                            new XAttribute( "name", "start" ),
                                pointConv.ConvertToInvariantString( Start )
                                )
                    );
            }
        }

        public void LoadFromXmlRepresentation( XElement e )
        {
            Debug.Assert( e.Name == "FrameGenerator" );
            Debug.Assert( e.Attribute( "type" ).Value == "CELL" );

            TypeConverter sizeConv = TypeDescriptor.GetConverter( typeof( Size ) );
            TypeConverter pointConv = TypeDescriptor.GetConverter( typeof( Point ) );

            foreach ( XElement param in e.Descendants( "param" ) )
            {
                if ( param.Attribute( "name" ).Value == "cell-size" )
                {
                    CellSize = (Size) sizeConv.ConvertFromInvariantString( param.Value );
                }
                else if ( param.Attribute( "name" ).Value == "cell-stride" )
                {
                    CellStride = (Size) sizeConv.ConvertFromInvariantString( param.Value );
                }
                else if ( param.Attribute( "name" ).Value == "start" )
                {
                    Start = (Point) pointConv.ConvertFromInvariantString( param.Value );
                }
            }
        }

        /// <summary>
        /// Returns the same as the FriendlyName property.
        /// </summary>
        public override string ToString()
        {
            return FriendlyName;
        }
        #endregion
    }
}
